#include <map>
#include <string>
#include "Animation.h"
#include "AnimatorHolder.h"
#include "AnimationFilmHolder.h"
#include "Timer.h"
#include "Popeye.h"
#include "HeartHolder.h"
#include "GameState.h"
#include "AudioHolder.h"

enum popeyeMode Popeye::Mode;

Popeye::Popeye(Dim _x, Dim _y, AnimationFilm* film, 
	FrameRangeAnimation *_walkAnimation, FrameRangeAnimator *_walkAnimator,
	FrameRangeAnimation *_punchAnimation, FrameRangeAnimator *_punchAnimator,
	MovingAnimation *_fallAnimation, MovingAnimator *_fallAnimator,
	MovingAnimation *_deathAnimation, MovingAnimator *_deathAnimator): 
	life(2), Sprite(_x,_y,film),
	walkAnimation(_walkAnimation), walkAnimator(_walkAnimator),
	punchAnimation(_punchAnimation), punchAnimator(_punchAnimator),
	fallAnimation(_fallAnimation), fallAnimator(_fallAnimator), 
	deathAnimation(_deathAnimation), deathAnimator(_deathAnimator){
	frameNo = currFilm->GetTotalFrames();
	SetFrame(0);
	canMove = true;
	isFalling = false;
	isClimbing = false;
	isInvisible = false;
	isPunching = false;
	Mode = normal;
	tickAnimation = new TickAnimation(0, 12350, 1, Dummy, this);
	timerTick = new TimerTickAnimator(tickAnimation);
	timerTick->SetOnFinish(SpinachModeCompleted, this);
	AnimatorHolder::Register(timerTick);
	flashAnimation = new FlashingAnimation(10, 200, 200, 0);
	flashAnimator = new FlashingAnimator();
}

Popeye::~Popeye(void) {
}

bool Popeye::OnPlatform() {
	Platform *p = PlatformHolder::CheckPlatformCollision(this);
	if(p) return true;
	return false;
}

//Horizontal movement
void Popeye::MoveHorizontal(int dx) {
	if(!canMove) return;
	SetWalkFilm();
	if(((Platform *)platform)->CollidesWithEdge(this, dx)) {
		StopMoving();
		return;
	}
	//need to check where he will be after moving, not where he is now
	if (!isFalling) {
		SpinachBox *sbox = ((Platform *)platform)->CollidesWithSpinachBox(this, dx);
		if(sbox) {
			sbox->AlignLeftOf(this);
			dx = 0;
		}
		if(walkAnimator->HasFinished()) {
			walkAnimator->Start(this, walkAnimation, Timer::getgametime());
			AnimatorHolder::MarkAsRunning(walkAnimator);
			walkAnimation->SetDx(dx);

			if(dx < 0) 
				walkAnimation->SetFlipH(true);
			else if(dx > 0)
				walkAnimation->SetFlipH(false);
		}
	}
	if (!OnPlatform()) {
		fallAnimation->SetFlipH(walkAnimation->GetFlipH());
		StopMoving();
		isFalling = true;
		NotifyStartFalling();
	}
}

void Popeye::StopMoving(void) {
	if(!walkAnimator->HasFinished()) {
		walkAnimator->Stop();
		AnimatorHolder::MarkAsSuspended(walkAnimator);
		walkAnimation->SetDx(0);
	}
}

void Popeye::Move(int dx, int dy) {
	if (!isFalling && !isClimbing) {
		//assert(dx == 0);
		Platform *p = PlatformHolder::CheckPlatformCollision(this);
		if (p) {
			this->platform = p;
			p->AlignWithPlatform(this);
		}
	}
	else if(isFalling) {
		Platform *p = PlatformHolder::CheckPlatformCollision(this);
		if (p) {
			dy = 0;
			this->platform = p;
			p->AlignWithPlatform(this);
			NotifyStopFalling();
		}
		else {
			dy += ++velocity;
		}
	}
	Sprite::Move(dx, dy);
}

/*
void Popeye::Teleport(void) {
	if() {
		}
}
*/
void Popeye::NotifyStartFalling() {
	velocity = 0;
	DisableMovement();
	fallAnimator->Start(this, fallAnimation, Timer::getgametime());
	AnimatorHolder::MarkAsRunning(fallAnimator);
}

void Popeye::NotifyStopFalling() {
	isFalling = false;
	EnableMovement();
	fallAnimator->Stop();
	AnimatorHolder::DelayedSuspention(fallAnimator);
}

//Vertical movement
void Popeye::MoveUp(void) {
	StopMoving();
	if (canMove) {
		if (platform) {
			if (stair = ((Platform*)platform)->CollidesWithGoingUpStair(this)) {
				if(((Stair *)stair)->GetStairType().compare("VerticalStair") == 0)
					SetVerticalClimbFilm();
				else
					SetDiagonalClimbUpFilm();
				isClimbing = true;
				platform = (Platform *) 0;
				((Stair *)stair)->AlignUp(this);
				((Stair *)stair)->AnimateUp(this);
			}
		}
	}
	else if (stair) {
		if(((Stair *)stair)->GetStairType().compare("VerticalStair") == 0)
			SetVerticalClimbFilm();
		else
			SetDiagonalClimbUpFilm();
		((Stair *)stair)->AnimateUp(this);
	}
}

void Popeye::MoveDown(void) {
	StopMoving();
	if (canMove) {
		if (platform) {
			if (stair = ((Platform*)platform)->CollidesWithGoingDownStair(this)) {
				if(((Stair *)stair)->GetStairType().compare("VerticalStair") == 0)
					SetVerticalClimbFilm();
				else
					SetDiagonalClimbDownFilm();
				isClimbing = true;
				platform = (Platform *) 0;
				((Stair *)stair)->AlignDown(this);
				((Stair *)stair)->AnimateDown(this);
			}
		}
	}
	else if (stair) {
		if(((Stair *)stair)->GetStairType().compare("VerticalStair") == 0)
			SetVerticalClimbFilm();
		else
			SetDiagonalClimbDownFilm();
		((Stair *)stair)->AnimateDown(this);
	}
}

bool Popeye::Punches(void) {
	return isPunching;
}

void Popeye::PunchCompleted(Animator *animator, void *closure) {
	Popeye *player = (Popeye *)closure;
	player->EnableMovement();
	player->isPunching = false;
	if(player->GetFlipH()) 
		player->SetX(player->GetX()+28); //This only works for popeye, brutus will need another value
	AnimatorHolder::DelayedSuspention(animator);
	player->SetWalkFilm();
}

void Popeye::Punch(void) {
	if(isClimbing) return;
	if(punchAnimator->HasFinished()) {
		DisableMovement();
		SetPunchFilm();
		if(GetFlipH()) 
			SetX(GetX()-28);
		punchAnimator->SetOnFinish(PunchCompleted, (void*)this);
		punchAnimator->Start(this, punchAnimation, Timer::getgametime());
		AnimatorHolder::MarkAsRunning(punchAnimator);
		isPunching = true;
	}
	if (!OnPlatform()) {
		isFalling = true;
		NotifyStartFalling();
	}
}

void Popeye::SpinachModeCompleted(Animator *animator, void *closure) {
	Popeye *player = (Popeye *)closure;
	player->changeMode();
	animator->Stop();
	AnimatorHolder::DelayedSuspention(animator);
}

void Popeye::Dummy(void *closure) {
	AudioHolder::ChangeToSound("level01");
}

void Popeye::changeMode(void) {
	if(Mode == normal) {
		Mode = spinach;
		timerTick->Start(Timer::getgametime());
		AnimatorHolder::MarkAsRunning(timerTick);
		HeartHolder::PauseHearts(Timer::getgametime());
		AudioHolder::ChangeToSound("spinach");
	}
	else {
		Mode = normal;
		HeartHolder::ResumeHearts();
	}
}

enum popeyeMode Popeye::getMode(void) {
	return Mode;
}

void Popeye::removeLife(void) { life--; }

unsigned int Popeye::getLives(void) { return life; }

void Popeye::CollisionAction(Sprite *s) {
};

void Popeye::InvinsibilityEnded(Animator *anim, void *data) {
	Popeye *p = (Popeye *)data;
	p->setInvinsibility(false);
	p->SetVisibility(true);
	anim->Stop();
	AnimatorHolder::DelayedSuspention(anim);
}

void Popeye::GotHit() {
	if(isInvisible) return;
	this->life--;
	if(getLives() == 0)
		Die();
	else {
		//printf("I'm the invisible man\n");
		flashAnimator->SetOnFinish(InvinsibilityEnded, this);
		flashAnimator->Start(this, flashAnimation, Timer::getgametime());
		AnimatorHolder::MarkAsRunning(flashAnimator);
		isInvisible = true;
	}
}

void Popeye::Die(void) {
	DisableMovement();
	walkAnimator->Stop();
	punchAnimator->Stop();
	fallAnimator->Stop();
	this->Mode = dead;
	AudioHolder::ChangeToSound("death");
	//GameState::makeKilled();
}

void Popeye::ResetToDefaultFilm(void) {
	if(Mode == spinach)
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_walk");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_walk");
} 

void Popeye::SetWalkFilm(void) {
	if(Mode == spinach)
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_walk");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_walk");
}

void Popeye::SetPunchFilm(void) {
	if(Mode == spinach)
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_punch");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_punch");
};
	
void Popeye::SetVerticalClimbFilm(void) {
	if(Mode == spinach) 
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_climb_vertical");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_climb_vertical");
};

void Popeye::SetDiagonalClimbUpFilm(void) {
	if(Mode == spinach) 
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_climbup_diagonal");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_climbup_diagonal");
};
	
void Popeye::SetDiagonalClimbDownFilm(void) {
	if(Mode == spinach) 
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_spinach_climbdown_diagonal");
	else
		currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_climbdown_diagonal");
};

void Popeye::SetDeathFilm(void) {
	currFilm = (AnimationFilm *)AnimationFilmHolder::GetSingleton()->GetFilm("popeye_killed");
};

bool Popeye::isDead(void) {
	if(Mode == dead) return true;
	return false;
};

void Popeye::PlayDeathAnimation(void) {
	if(Mode != dead) return;
	SetDeathFilm();
	deathAnimator->Start(this, punchAnimation, Timer::getgametime());
	AnimatorHolder::MarkAsRunning(deathAnimator);
}